from random import randint

class Voronoi(object):
    """ Class whose task is to create a Voronoi-like diagram.
        The diagram is a 2-D array containing the distances(*) between
        the currently pointed cell and 2 closest feature points.
        (*) The distances are calculated according to the formula given
        in the paper 'Realtime Procedural Terrain Generation' (see simplifiedBibliography.txt):
        distance = C1 * d1 + C2 * d2, 
        where C1 = -1, C2 = 1 (these are constant), d1 is the distance to the closest
        feature point, d2 is the distance to the second closest feature point.
        Feature point, around which the cells of the diagram are formed, are chosen
        randomly.
        In order to generate a Voronoi-like diagram, you must instantiate this
        class only once(!) and then call createNewDiagram() from its instance how
        many times you wish.
        
        Class attribs:
        _featurePointsNumber - the number of feature points
        _size - the size of the grid (the length of a side of the square grid)
        _n1 - the array containing X coordinates of feature points
        _n2 - the array containing Y coordinates of feature points
        _matrix - 2-D array that will contain the diagram
        
        _secondShortest - variable used in calculations of the distance:
         stores the distance between the given point and second closest feature point
        
        _shortest - variable used in calculations of the distance:
         stores the distance between the given point and the closest feature point
        
        _distance = - variable used in calculations of the distance:
         stores the temporary distance in order to compare it with the shorstest and second shortest
         
        _C1 - a constant used in formula(see above) (always equal -1)
        _C2 - a constant used in formula(see above) (always equal 1)
        _distances - tuple used in calculating the cell's value;
         the first value is the distance to the closest feature point, the second one is 
            the distance to the second closest feature point.
        
        _NIL - a constant assigned to the cell whose value hasn't been calculated yet
    """
    
    _featurePointsNumber = 15
    _size = 100
    _n1 = []
    _n2 = []
    _matrix = None
    
    _secondShortest = 0
    _shortest = 0
    _distance = 0
    
    _C1 = -1
    _C2 = 1
    _distances = (0, 0)
    
    _NIL = 0
    
    def __init__(self, iSize = 100):
        """ Init.
            
            params:
            iSize - the size of a grid; by default, it's 100
        """
        self._size = iSize
        
        
    def getSize(self):
        """ The 'getter' method, returning the size of the diagram.
        """
        
        return self._size
        
    def setSize(self, iNewSize):
        """ The 'setter' method, setting the size of the diagram.
        
        params:
        iNewSize - the new size of the diagram
        """
        
        self._size = iNewSize
        
    def getFeaturePointsNumber(self):
        """ The 'getter' method, returning the number of feature points
        """
        return self._featurePointsNumber
        
    def setFeaturePointsNumber(self, iNewNumber):
        """ The 'setter' method, setting the number of feature points
        """
        self._featurePointsNumber = iNewNumber
        
    def _dist(self, x1, x2, y1, y2):
        """ The method used for calculating the distance between two points.
            The first points has coordinates (x1, x2), while the second one has (y1, y2).
            The distance is calculated as Euclidean metric except the square root.
            
            params:
            x1 - the coordinate X of the first point
            x2 - the coordinate Y of the first point
            y1 - the coordinate X of the second point
            y2 - the coordinate Y of the second point
        """
        
        dx = x1 - y1
        dy = x2 - y2
        
        return (((dx * dx) + (dy * dy)))
        
    def _distFromFeaturePoints(self, x1, x2):
        """ The method used to determine which 2 feature points are the closest to 
            the given point. It modifies the tuple _distances containing two values: the first 
            one is the distance to the closest feature point, the second one is 
            the distance to the second closest feature point.
            
            params:
            x1 - the coordinate X of the point
            x2 - the coordinate Y of the point
        """
    
        self._secondShortest = self._size * self._size
        self._shortest = self._size * self._size
        self._distance = 0
        
        # we're checking the distances to all the feature points,
        # and then assigning the shortest and second shortest
        for i in range(self._featurePointsNumber):
            
            self._distance = self._dist(x1, x2, self._n1[i], self._n2[i])
        
            if self._distance <= self._shortest:
                self._secondShortest = int(self._shortest)
                self._shortest = int(self._distance)
    
            elif (self._distance <= self._secondShortest) and (self._distance > 0):
                self._secondShortest = int(self._distance)
    
        return (self._shortest, self._secondShortest)
    
    
    def createNewDiagram(self, iSize = None):
        """ The method creating new Voronoi-like diagram.
            If the parameter iSize is 'None', then the old size of the grid is used.
            
            params:
            iSize - (optional) new size of the grid
        """
        
        if iSize is not None:
            self._size = iSize
            
        # upon each call, the new set of feature points is chosen
        self._n1 =  [(randint(0, self._size-1)) for j in range(self._featurePointsNumber)]
        self._n2 =  [(randint(0, self._size-1)) for j in range(self._featurePointsNumber)]

        # upon each call, the new matrix is created
        self._matrix = [[self._NIL for i in range(self._size)] for j in range(self._size)]

        # now we need to determine the shortest possible distance between 
        # the given point (i, j) and some feature point
        # then do some calculations and store the result
        for i in range(self._size):
            for j in range(self._size):
                
                self._distances = self._distFromFeaturePoints(i, j)
                self._matrix[i][j] = self._C1 * self._distances[0] + self._C2 * self._distances[1]
                
        return self._matrix


#### the code below is not the essential part of the class - it's just for testing
# optional - representation via matplotlib

#d = Voronoi()
#map = d.createNewDiagram(100)
#import matplotlib.pyplot as plt
#
#plt.imshow(map)
#plt.show()
